#pragma once

#include <iostream>
#include <cstdio>
#include <string>
#include <ctime>
#include <stdarg.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <fstream>

bool gIsSave = false;
const std::string glogname = "log.txt";
// 1. 日志等级
enum Level
{
    DEBUG = 0,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

std::string LevelToString(int level)
{
    switch (level)
    {
    case Level::DEBUG:
        return "Debug";
    case Level::INFO:
        return "Info";
    case Level::WARNING:
        return "Warning";
    case Level::ERROR:
        return "Error";
    case Level::FATAL:
        return "Ratal";
    default:
        return "Unkonw";
    }
}

std::string GetTimeString()
{
    time_t curr_time = time(nullptr);
    struct tm *format_time = localtime(&curr_time);
    char timebuffer[1024];
    snprintf(timebuffer, sizeof(timebuffer), "%d-%d-%d %d:%d:%d",
             format_time->tm_year + 1900,
             format_time->tm_mon + 1,
             format_time->tm_mday,
             format_time->tm_hour,
             format_time->tm_min,
             format_time->tm_sec);
    return timebuffer;
}

// 
void SaveFile(const std::string &filename, const std::string &message)
{
    std::ofstream ofs(filename, std::ios::app);
    if (!ofs.is_open())
    {
        return;
    }
    ofs << message;
    ofs.close();
}

// 2. 日志格式
// 日志的等级 时间 代码所在文件/行数  日志内容

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void LogMessage(std::string filename, int line, bool save, int level, const char *format, ...)
{
    std::string levelstr = LevelToString(level);
    std::string timestr = GetTimeString();
    pid_t selfid = getpid();

    // 1. 知识点1 使用vsnprintf拿到可变参数部分
    char buffer[1024];
    va_list arg;
    va_start(arg, format);
    vsnprintf(buffer, 1024, format, arg);
    va_end(arg);
    std::string message = "[" + timestr + "]" + "[" + levelstr + "]" + \
    "[pid:" + std::to_string(selfid) + "]" + "[" + filename + "]" + "[" + std::to_string(line) + "] :" + buffer;

    pthread_mutex_lock(&lock);
    if (!gIsSave)
        std::cout << message;
    else
    {
        SaveFile(glogname, message);
    }
    pthread_mutex_unlock(&lock);

    // std::cout << levelstr << " " << timestr <<" : " << filename << " : " << line <<  ":" << buffer <<std::endl;
}

// 3. 知识点3， 宏支持可变参数 用 ##__VA_ARGS__代替
// 如果宏替换比较大的时候建议用do{;}while(0)替换
#define LOG(level, format, ...)                                                \
    do                                                                         \
    {                                                                          \
        LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__); \
    } while (0)

// 放入文件
#define EnableFile()    \
    do                  \
    {                   \
        gIsSave = true; \
    } while (0)

// 在显示器打印
#define EnableScream()   \
    do                   \
    {                    \
        gIsSave = false; \
    } while (0)

// 2. 知识点3 提取可变参数部分
// 默认传递的是整形，num是可变参数的个数
void Test(int num, ...)
{
    va_list arg;
    while (num)
    {
        int data = va_arg(arg, int);
    }

    va_end(arg);
}